#include "send_mail.h"
#include "change64.h"

SMTP*
smtp_init(void) {
	SMTP *stemp = (SMTP *)malloc(sizeof(SMTP));
	if(stemp == NULL) {
		return NULL;
	}
	stemp->connect_tv = 
		(struct timeval *)malloc(sizeof(struct timeval));
	stemp->connect_tv->tv_sec = 0;
	stemp->connect_tv->tv_usec = 0;
	stemp->recv_tv = 
		(struct timeval *)malloc(sizeof(struct timeval));
	stemp->recv_tv->tv_sec = 0;
	stemp->recv_tv->tv_usec = 0;
	return stemp;
}

int 
set_server_addr(SMTP *smtpr, const char *server_addr) {
	if(strlen(server_addr) >= sizeof(smtpr->server_addr) ) {
		serrno = SET_ERROR; //todo: define SET_ERROR 
		return SYS_ERROR ;	
	}
	strcpy(smtpr->server_addr, server_addr);	
	return SMTP_SUCCESS;
}

int 
set_username(SMTP *smtpr, const char *username) {
	if(strlen(username) >= sizeof(smtpr->username)) {
		serrno = SET_ERROR;
		return SYS_ERROR;
	}
	strcpy(smtpr->username, username);
	return SMTP_SUCCESS;
}

int 
set_passswd(SMTP *smtpr, const char *passwd) {
	if(strlen(passwd) >= sizeof(smtpr->passwd)) {
		serrno = SET_ERROR;
		return SYS_ERROR;
	}
	strcpy(smtpr->passwd, passwd);
	return SMTP_SUCCESS;
}

int 
set_Recv(SMTP *smtpr, const char *Recv) {
	if(strlen(Recv) >= sizeof(smtpr->Recv)) {
		serrno = SET_ERROR;
		return SYS_ERROR;
	}
	strcpy(smtpr->Recv, Recv);
	return SMTP_SUCCESS;
}

int 
set_connect_tv(SMTP *smtpr, int sec) {
	smtpr->connect_tv->tv_sec = sec;
	return SMTP_SUCCESS;
}

int
set_recv_tv(SMTP *smtpr, int sec) {
	smtpr->recv_tv->tv_sec = sec;
	return SMTP_SUCCESS;
}

Mail*
mail_init(void)  {
	Mail *mtemp = (Mail*)malloc(sizeof(Mail));
	if(mtemp == NULL) {
		return NULL;
	}
	return mtemp;
}

int 
set_title(Mail *mpr, const char *title) {
	if(strlen(title) >= sizeof(mpr->title)) {
		serrno = SET_ERROR;
		return SMTP_ERROR;
	}
	strcpy(mpr->title, title);
	return SMTP_SUCCESS;
}

int
set_content(Mail *mpr, const char *content) {
	if(strlen(content) >= sizeof(mpr->content)) {
		serrno = SET_ERROR;
		return SMTP_ERROR;
        }
	strcpy(mpr->content, content);
	return SMTP_SUCCESS;
}

/*
 * 非阻塞连接
 */
int 
connect_nonb(struct sockaddr* sp, 
		socklen_t slen, int nsec, SMTP *smtpr)  {
	int flags;
	int error = 0;
	int n;
	fd_set rset, wset; //分别定义一组读的，一组写的文件描述符集
	socklen_t len;
	struct timeval tval;
	//取出当前套接字状态
	flags = fcntl(smtpr->sockfd, F_GETFL, 0);
	fcntl(smtpr->sockfd, F_SETFL, flags | O_NONBLOCK);
	if(( n = connect(smtpr->sockfd, sp, slen ))< 0) {
		
		/*
		 * EINPROGRESS表示连接启动但尚未建立
		 */
		if(errno != EINPROGRESS) {
			return SYS_ERROR;
		}
	}
	/*
	 * 连接已建立
	 */
	if(n == 0)
	{
		fcntl(smtpr->sockfd, F_SETFL, flags);
		return SMTP_SUCCESS;
	}
	FD_ZERO(&rset);
	FD_SET(smtpr->sockfd, &rset);
	wset = rset;

	tval.tv_sec = nsec;
	tval.tv_usec = 0;
	if(select(smtpr->sockfd+1, &rset, &wset, NULL,
			nsec? &tval: NULL) == 0) {
		close(smtpr->sockfd);
		/*
		 * 无法在规定时间建立连接
		 */
		errno = ETIMEDOUT;
		return SYS_ERROR;
	}
	if(FD_ISSET(smtpr->sockfd, &rset) 
			|| FD_ISSET(smtpr->sockfd, &wset)) {
		len = sizeof(error);
		if(getsockopt(smtpr->sockfd, 
				SOL_SOCKET, 
				SO_ERROR,
				&error,
				&len) < 0) {
			return SYS_ERROR;
		}
	}
	else {
		return SYS_ERROR;
	}
	//恢复套接字状态
	flags = fcntl(smtpr->sockfd, F_SETFL, flags);
	return SMTP_SUCCESS;
}

int 
read_msg(SMTP *smtpr) {
	memset(smtpr->buf, 0, sizeof(smtpr->buf));
	if(recv(smtpr->sockfd,smtpr->buf, sizeof(smtpr->buf), 0) == -1) {
		return SYS_ERROR;
	}
	//处理smtp服务器返回内容
	return deal_return(smtpr);
}

int
send_msg(SMTP *smtpr, char *buf) {
	if(send(smtpr->sockfd, buf, strlen(buf), 0) == -1)
	{
		return SYS_ERROR;
	}
	return SMTP_SUCCESS;
}

/*
 *处理smtp服务器返回内容
 */
int
deal_return(SMTP *smtpr) {
	char return_num[10] = {0};
	strncpy(return_num, smtpr->buf, 3);	
	char num [9][5] = {"220", "220", "251", "354", "334",
			"221", "235", "250"};
	int i = 0;
	for(i = 0; i < 9; i++) {
		if(0 == strcmp(return_num,num[i])) {
			break;
		}
	}
	/*
	 * 根据不同的状态码给出的错误
	 */
	if(i == 9) {
		if(0 == strcmp(return_num, "421")) {
			serrno = SUNAVAILABLE;
		}
		else if(0 == strcmp(return_num, "450") &&
				0 == strcmp(return_num, "452") &&
				0 == strcmp(return_num, "550") &&
				0 == strcmp(return_num, "553")) {
			serrno = SUNTAKEN;
		}
		else if(0 == strcmp(return_num, "451") &&
				0 == strcmp(return_num, "552")) {
			serrno = SABORTED;
		}
		else if(0 == strcmp(return_num, "500") &&
				0 == strcmp(return_num, "501")) {
			serrno = SSYNERROR;
		}
		else if(0 == strcmp(return_num, "502") &&
				0 == strcmp(return_num, "504")) {
			serrno = SNOIMPLEMENTED;		
		}
		else if(0 == strcmp(return_num, "503")) {
			serrno = SBADCOMMANDS;
		}
		else if(0 == strcmp(return_num, "551")) {
			serrno = SNOLOCAL;
		}
		else if(0 == strcmp(return_num, "554")) {
			serrno = STSFAILED;
		}
		else {
			serrno = SERROR;
		}
		return SMTP_ERROR;
	}
	return SMTP_SUCCESS;
}

/*
 * 流程的第一步建立连接
 */
int 
smtp_connect(struct sockaddr *sp, socklen_t slen,
		int nsec, SMTP *smtpr) {
	if(SYS_ERROR == connect_nonb(sp, slen, nsec, smtpr)) {
		return SYS_ERROR;
	}
	return read_msg(smtpr);
}

int 
hello(SMTP *smtpr, Mail *mpr) {
	char temp[105] = {0};
	sprintf(temp, "%s %s\r\n", "ehlo", smtpr->username);
	if(SYS_ERROR == send_msg(smtpr,temp)) {
		return SYS_ERROR;
	}
	return read_msg(smtpr);
}

int 
auth(SMTP *smtpr, Mail *mpr) {
	char temp[105] = {0};
	sprintf(temp, "%s\r\n", "AUTH LOGIN");
	if(SYS_ERROR == send_msg(smtpr,temp)) {
		return SYS_ERROR;
	}
	return read_msg(smtpr);
}

int
login(SMTP *smtpr, Mail *mpr) {
	int return_num;
	char temp[105] = {0};
	to64(smtpr->username);
	sprintf(temp, "%s\r\n", change);
	if(SYS_ERROR == send_msg(smtpr,temp)) {
		return SYS_ERROR;
	}
	return_num = read_msg(smtpr);
	if(return_num != SMTP_SUCCESS) {
		return return_num;
	}
	memset(temp, 0, sizeof(temp));
	to64(smtpr->passwd);
	sprintf(temp, "%s\r\n", change);
	if(SYS_ERROR == send_msg(smtpr,temp)) {
		return SYS_ERROR;
	}
	return read_msg(smtpr);
}

int 
from_to(SMTP *smtpr, Mail *mpr) {
	char temp[105] = {0};
	int return_num;
	sprintf(temp, "mail from <%s>\r\n", smtpr->username);
	if(SYS_ERROR == send_msg(smtpr,temp)) {
		return SYS_ERROR;
	}
	return_num = read_msg(smtpr);
	if(return_num != SMTP_SUCCESS) {
		return return_num;
	}
	memset(temp, 0, sizeof(temp));
	sprintf(temp, "rcpt to <%s>\r\n", smtpr->Recv);
	if(SYS_ERROR == send_msg(smtpr,temp)) {
		return SYS_ERROR;
	}
	return read_msg(smtpr);
}

int 
data(SMTP *smtpr, Mail *mpr) {
	char temp[40] = {0};
	sprintf(temp, "%s\r\n", "data");
	if(SYS_ERROR == send_msg(smtpr,temp)) {
		return SYS_ERROR;
	}
	return read_msg(smtpr);
}

int
mail(SMTP *smtpr, Mail *mpr) {
	char temp[105] = {0};
	sprintf(temp, "from:%s\r\n", smtpr->username);
	if(SYS_ERROR == send_msg(smtpr,temp)) {
		return SYS_ERROR;
	}
	memset(temp, 0, sizeof(temp));
	sprintf(temp, "%s\r\n\r\n", mpr->title);
	if(SYS_ERROR == send_msg(smtpr,temp)) {
		return SYS_ERROR;
	}
	memset(temp, 0, sizeof(temp));
	sprintf(temp, "%s\r\n.\r\n", mpr->content);
	if(SYS_ERROR == send_msg(smtpr,temp)) {
		return SYS_ERROR;
	}
	return read_msg(smtpr);
}

int
quit(SMTP *smtpr, Mail *mpr) {
	char temp[40] = {0};
	sprintf(temp, "%s\r\n", "QUIT");
	if(SYS_ERROR == send_msg(smtpr,temp)) {
		return SYS_ERROR;
	}
	return read_msg(smtpr);
}

int
send_mail(SMTP *smtpr, Mail *mpr) {
	int i;
	struct sockaddr_in server;
	struct servent *sptr;
	struct hostent *hptr;
	//流程链
	int (*process_chain[7])(SMTP *, Mail *);
	process_chain[0] = &hello;
	process_chain[1] = &auth;
	process_chain[2] = &login;
	process_chain[3] = &from_to;
	process_chain[4] = &data;
	process_chain[5] = &mail;
	process_chain[6] = &quit;

	smtpr->sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(smtpr->sockfd == -1) {
		return SYS_ERROR;
	}
	server.sin_family = AF_INET;
	if((sptr = getservbyname("smtp", "tcp")) != NULL) {
		server.sin_port = sptr->s_port;
	}
	if((hptr = gethostbyname(smtpr->server_addr)) != NULL) {
		memcpy(&server.sin_addr, hptr->h_addr_list[0], 
				hptr->h_length);
	}
	else
	{
		return SYS_ERROR;
	}
	if(smtp_connect((struct sockaddr *)&server, sizeof(server), 
			smtpr->connect_tv->tv_sec, smtpr) == SYS_ERROR) {
		return SYS_ERROR;
	}
	//设置接受超时
	if(-1 == setsockopt(smtpr->sockfd, SOL_SOCKET, SO_RCVTIMEO,
			smtpr->recv_tv, sizeof(struct timeval))) {
		return SYS_ERROR;
	}
	// 循环执行流程链
	for(i = 0; i < 7; i++) {
		int temp = process_chain[i](smtpr, mpr);		
		if(temp != SMTP_SUCCESS) {
			printf("%d\n", i);
			return temp;
		}
	}
	return SMTP_SUCCESS;
}

void
smtp_destory(SMTP *smtpr, Mail *mpr) {
	free(smtpr);
	free(mpr);
}

